using Cysharp.Threading.Tasks;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEngine.Networking;

public class WebRequest : IWebRequest
{
    TimeoutController timeoutController = new TimeoutController();
    public virtual void OnDestroy()
    {
        _PostConnectionCancelToken.Dispose();
    }

    protected CancellationTokenSource _PostConnectionCancelToken = new();

    #region Get_Request
    public void Get(string url, Action<string> onSuccess, Action<string> onError, int timeout = 1)
    {
        GetRequest(url, onSuccess, onError, timeout).Forget();
    }
    public async UniTask Get_Async(string url, Action<string> onSuccess, Action<string> onError, int timeout = 1)
    {
         await GetRequest(url, onSuccess, onError, timeout);
    }
    private async UniTask GetRequest(string url, Action<string> onSuccess, Action<string> onError, int timeout)
    {
        //Debug.Log("start Time:" + (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000);
        using (UnityWebRequest unityWebRequest = UnityWebRequest.Get(url))
        {
            unityWebRequest.timeout = timeout;
            try
            {
                var (cancelOrFailed, result) = await unityWebRequest.SendWebRequest()
                    .WithCancellation(timeoutController.Timeout(TimeSpan.FromSeconds(1))).SuppressCancellationThrow();
                if (cancelOrFailed && result == null)
                {
                    onError($"Connection to {url} is Cancelled");
                }
                else if (!cancelOrFailed)
                {
                    onSuccess(result.downloadHandler.text);
                }

            }
            catch (UnityWebRequestException ex)
            {
                if (ex.UnityWebRequest.result == UnityWebRequest.Result.ConnectionError)
                {
                    onError("Cannot connect to destination host");
                }
                else if (ex.UnityWebRequest.result == UnityWebRequest.Result.ProtocolError)
                {
                    onError("Destination host returned an error response\n" + ex.ToString());
                }
                else
                    Debug.LogError(ex.ToString());
            }

            unityWebRequest.Dispose();
        }
    }
    #endregion

    #region Post_Request

    public void Post_Async(string url, Action<string> onError, Action<string> onSuccess, string postBody, int timeout = 1)
    {
        UniTask waitTask = new UniTask<string>();
        waitTask = PostRequest(url, onError, onSuccess, postBody, timeout);
    }

    async UniTask PostRequest(string url, Action<string> onError, Action<string> onSuccess, string postBody, int timeout = 1)
    {
        using UnityWebRequest _PostRequest = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST);
        _PostConnectionCancelToken = new();
        _PostConnectionCancelToken.CancelAfterSlim(TimeSpan.FromSeconds(timeout));
        var (cancelOrFailed, result) = await UniTaskPostRequest(url, _PostConnectionCancelToken.Token, _PostRequest, postBody, timeout);
        if (cancelOrFailed && result != null)
        {
            onError(result.error);
        }
        else if (!cancelOrFailed)
        {
            onSuccess(result.downloadHandler.text);
        }
        //Profiler.EndSample();
        _PostRequest.Dispose();
    }

    private async UniTask<(bool cancelOrFailed, UnityWebRequest result)> UniTaskPostRequest(string url, CancellationToken token, UnityWebRequest _PostRequest, string postBody, int timeout = 1)
    {
        //_PostRequest = UnityWebRequest.Post(url, UnityWebRequest.kHttpVerbPOST);
        byte[] bodyRaw = Encoding.UTF8.GetBytes(postBody);
        _PostRequest.uploadHandler = new UploadHandlerRaw(bodyRaw);
        _PostRequest.downloadHandler = new DownloadHandlerBuffer();
        _PostRequest.SetRequestHeader("Content-Type", "application/json");

        _PostRequest.timeout = timeout;

        return await _PostRequest.SendWebRequest().WithCancellation(token).SuppressCancellationThrow();
    }
    #endregion
}

public interface IWebRequest : IGetAsync, IPostAsync
{
    void OnDestroy();
}

public interface IGetAsync
{
    public void Get(string url, Action<string> onSuccess, Action<string> onError, int timeout = 1);
    public UniTask Get_Async(string url, Action<string> onSuccess, Action<string> onError, int timeout = 1);
}
public interface IPostAsync
{
    public void Post_Async(string url, Action<string> onSuccess, Action<string> onError, string postBody, int timeout = 1);
}
